﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace SmartAnnotation
{
    class Annotation
    {
        enum stateType
        {
            NONE,                 // the class doesn't do anything
            RESIZING_LEFT,        // resizing left border is in progress
            RESIZING_RIGHT,       // resizing right border is in progress
            RESIZING_TOP,         // resizing top border border is in progress
            RESIZING_BOTTOM,      // resizing bottom border is in progress
            //RESIZING_NWSE,
            //RESIZING_NESW,
            MOVE,                 // move is in progress
            DRAW                  // draw new annotation is in progress
        }

        //*****************************************************
        // Private data members
        //*****************************************************
        #region Private Data Members
        // Current mouse position
        private int mouseX;
        private int mouseY;


        // Mouse position when the button was pressed
        private int mouseDownX;
        private int mouseDownY;


        // Mouse position when the button war released
        private int mouseUpX;
        private int mouseUpY;


        // Mouse state
        private bool mouseOnImage;
        private bool isMouseDown;


        // Class state - what the class is doing at a specific time
        private stateType classState;


        private LinkedList<AnnotationType> annotationList;
        private Label annotationText;
        private PictureBox pb;
        private VScrollBar vScrollBar;
        private HScrollBar hScrollBar;

        // Constants
        private const int MIN_WIDTH = 10;
        private const int MIN_HEIGHT = 10;
        #endregion
        //*****************************************************
        // Constructor(s)
        //*****************************************************

        public Annotation(PictureBox pBox,
                          VScrollBar vScrollBar,
                          HScrollBar hScrollBar)
        {
            // Init
            this.annotationList = new LinkedList<AnnotationType>();
            this.mouseX = 0;
            this.mouseY = 0;
            this.mouseDownX = 0;
            this.mouseDownY = 0;
            this.mouseUpX = 0;
            this.mouseUpY = 0;
            this.isMouseDown = false;
            this.mouseOnImage = false;
            this.classState = stateType.NONE;
            this.pb = pBox;
            this.vScrollBar = vScrollBar;
            this.hScrollBar = hScrollBar;

            // Register mouse handlers
            this.pb.MouseEnter += new System.EventHandler(MouseEnter_Handler);
            this.pb.MouseLeave += new System.EventHandler(MouseLeave_Handler);
            this.pb.MouseMove += new System.Windows.Forms.MouseEventHandler(MouseMove_Handler);
            this.pb.Paint += new System.Windows.Forms.PaintEventHandler(Paint_Handler);
            this.pb.MouseDown += new System.Windows.Forms.MouseEventHandler(MouseDown_Handler);
            this.pb.MouseUp += new System.Windows.Forms.MouseEventHandler(MouseUp_Handler);
            // Register scroll handlers
            this.vScrollBar.Scroll += new System.Windows.Forms.ScrollEventHandler(this.vScrollBar_Scroll);
            this.hScrollBar.Scroll += new System.Windows.Forms.ScrollEventHandler(this.hScrollBar_Scroll);
            // Register pictureBox handlers
            this.pb.SizeChanged += new System.EventHandler(this.pictureBox_SizeChanged);

            // Set scrollbars to right value.
            this.vScrollBar.Minimum = 0;
            if (pb.Height >= pb.Image.Height)
                this.vScrollBar.Maximum = 0;
            else
                this.vScrollBar.Maximum = pb.Image.Height - pb.Height;
            this.vScrollBar.Value = 0;

            this.hScrollBar.Minimum = 0;
            if (pb.Width >= pb.Image.Width)
                this.hScrollBar.Maximum = 0;
            else
                this.hScrollBar.Maximum = pb.Image.Width - pb.Width;
            this.hScrollBar.Value = 0;
        }

        //*****************************************************
        // Constructor(s)
        //*****************************************************

        private void DrawAnnotationDescription(Graphics g,
                                               string title,
                                               string text,
                                               int x,
                                               int y,
                                               int w,
                                               int h)
        {
            Pen outline = new Pen(Color.FromArgb(150, Color.White));
            SolidBrush box = new SolidBrush(Color.FromArgb(150, Color.Black));
            Brush textBrush = Brushes.White;
            Font titleFont = new Font("Arial", 8, FontStyle.Bold);
            Font textFont = new Font("Arial", 8, FontStyle.Regular);

            g.FillRectangle(box, x, y, w, h);
            g.DrawRectangle(outline, x + 1, y + 1, w - 2, h - 2);
            g.DrawLine(outline, x + 1, y + 4 + titleFont.Height, w - 2, y + 4 + titleFont.Height);

            g.DrawString(title, titleFont, textBrush, x + 5, y + 2);
            g.DrawString(text, textFont, textBrush, x + 5, y + 4 + titleFont.Height);
        }

        //*****************************************************
        // Getters & Setters
        //*****************************************************
        #region Getters and Setters

        public LinkedList<AnnotationType> AnnotationList
        {
            get { return annotationList; }
        }

        public Label AnnotationText
        {
            get { return annotationText; }
            set { this.annotationText = value; }
        }

        public PictureBox PB
        {
            set { this.pb = value; }
        }

        #endregion
        //*****************************************************
        // Handlers
        //*****************************************************
        #region Handlers

        public void MouseEnter_Handler(object sender, EventArgs e)
        {
            mouseOnImage = true;
            ((PictureBox)sender).Refresh();
        }

        public void MouseLeave_Handler(object sender, EventArgs e)
        {
            mouseOnImage = false;
            ((PictureBox)sender).Refresh();
        }

        public void MouseMove_Handler(object sender, EventArgs e)
        {
            bool cursorSet = false;
            string textToShow = "";
            MouseEventArgs me = (MouseEventArgs)e;
            int deltaX = me.X - mouseX;
            int deltaY = me.Y - mouseY;

            mouseX = me.X;
            mouseY = me.Y;

            foreach (AnnotationType at in annotationList)
            {
                at.UpdateScroll(hScrollBar.Value, vScrollBar.Value);

                if ((at.IsInMiddle(mouseX, mouseY) && stateType.NONE == classState) ||
                    classState == stateType.MOVE)
                {
                    // Change cursor to move & show the annotation text
                    textToShow = at.GetText();
                    pb.Cursor = Cursors.SizeAll;
                    cursorSet = true;

                    // See if it must be moved
                    if (at.IsEditing() && classState == stateType.MOVE)
                        at.Move(deltaX, deltaY);
                }
                /*
                else
                // Change cursor to size NWSE
                if ((at.IsInLeftMargin(mouseX, mouseY) && at.IsInTopMargin(mouseX, mouseY)) ||
                     (at.IsInRightMargin(mouseX, mouseY) && at.IsInBottomMargin(mouseX, mouseY))
                    )
                {
                    pb.Cursor = Cursors.SizeNWSE;
                    cursorSet = true;
                }
                else
                // Change cursor to size NESW
                if ((( (at.IsInLeftBorder(mouseX, mouseY) && at.IsInBottomBorder(mouseX, mouseY)) ||
                       (at.IsInRightBorder(mouseX, mouseY) && at.IsInTopBorder(mouseX, mouseY))
                      ) && classState == stateType.NONE
                     ) || classState == stateType.RESIZING_NESW
                    )
                {
                    pb.Cursor = Cursors.SizeNESW;
                    cursorSet = true;

                    if (at.IsEditing() && classState == stateType.RESIZING_NESW)
                    {
                    }

                }
                 */
                else
                    // Change cursor to size horizontally
                    if (((at.IsInLeftBorder(mouseX, mouseY) || at.IsInRightBorder(mouseX, mouseY)) &&
                         classState == stateType.NONE) ||
                        classState == stateType.RESIZING_LEFT ||
                        classState == stateType.RESIZING_RIGHT)
                    {
                        pb.Cursor = Cursors.SizeWE;
                        cursorSet = true;

                        if (at.IsEditing())
                        {
                            if (classState == stateType.RESIZING_LEFT)
                            {
                                at.Move(deltaX, 0);
                                at.Resize(-deltaX, 0);
                            }
                            else if (stateType.RESIZING_RIGHT == classState)
                            {
                                at.Resize(deltaX, 0);
                            }
                        }
                    }
                    else
                        // Change cursor to size vertically
                        if (((at.IsInTopBorder(mouseX, mouseY) || at.IsInBottomBorder(mouseX, mouseY)) &&
                             classState == stateType.NONE) ||
                            classState == stateType.RESIZING_TOP ||
                            classState == stateType.RESIZING_BOTTOM)
                        {
                            pb.Cursor = Cursors.SizeNS;
                            cursorSet = true;

                            if (at.IsEditing())
                            {
                                if (classState == stateType.RESIZING_TOP)
                                {
                                    at.Move(0, deltaY);
                                    at.Resize(0, -deltaY);
                                }
                                else
                                {
                                    at.Resize(0, deltaY);
                                }
                            }
                        }
            }

            // No cursor set => reset cursor
            if (!cursorSet)
            {
                pb.Cursor = Cursors.Arrow;
            }

            // Handle the annotation text
            //annotationText.Text = textToShow;

            ((PictureBox)sender).Refresh();
        }

        public void MouseDown_Handler(object sender, EventArgs e)
        {
            MouseEventArgs me = (MouseEventArgs)e;
            mouseDownX = me.X;
            mouseDownY = me.Y;
            isMouseDown = true;

            foreach (AnnotationType at in annotationList)
            {
                at.UpdateScroll(hScrollBar.Value, vScrollBar.Value);

                // Initiate move action
                if (at.IsInMiddle(mouseX, mouseY) &&
                    classState == stateType.NONE)
                {
                    at.SetEditing(true);
                    classState = stateType.MOVE;
                    break;
                }

                // Initiate resize action
                // -- left
                if (at.IsInLeftBorder(mouseX, mouseY) &&
                    classState == stateType.NONE)
                {
                    at.SetEditing(true);
                    classState = stateType.RESIZING_LEFT;
                }
                // -- right
                if (at.IsInRightBorder(mouseX, mouseY) &&
                    classState == stateType.NONE)
                {
                    at.SetEditing(true);
                    classState = stateType.RESIZING_RIGHT;
                }
                // -- top
                if (at.IsInTopBorder(mouseX, mouseY) &&
                    classState == stateType.NONE)
                {
                    at.SetEditing(true);
                    classState = stateType.RESIZING_TOP;
                }
                // -- bottom
                if (at.IsInBottomBorder(mouseX, mouseY) &&
                    classState == stateType.NONE)
                {
                    at.SetEditing(true);
                    classState = stateType.RESIZING_BOTTOM;
                }
            }

            if (classState == stateType.NONE)
            {
                // This mean the mouse was pressed on an emty space
                classState = stateType.DRAW;
            }

            ((PictureBox)sender).Refresh();
        }

        public void MouseUp_Handler(object sender, EventArgs e)
    {
        bool objChanged = false;
        MouseEventArgs me = (MouseEventArgs)e;
        mouseUpX = me.X;
        mouseUpY = me.Y;
        int x1, x2;
        int y1, y2;

        isMouseDown = false;

        if (mouseDownX < mouseUpX)
        {
            x1 = mouseDownX;
            x2 = mouseUpX;
        }
        else
        {
            x2 = mouseDownX;
            x1 = mouseUpX;
        }

        if (mouseDownY < mouseUpY)
        {
            y1 = mouseDownY;
            y2 = mouseUpY;
        }
        else
        {
            y2 = mouseDownY;
            y1 = mouseUpY;
        }

        if (classState != stateType.NONE)
            foreach (AnnotationType at in annotationList)
            {
                if (at.IsEditing())
                {
                    at.SetEditing(false);
                    objChanged = true;

                    // See if it was a click.
                    // If it was then open annotationInput to edit the annotation.
                    if (x2 - x1 == 0 &&
                        y2 - y1 == 0)
                    {
                        AnnotationInput inputBox = new AnnotationInput(at.GetTitle(),
                                                                       at.GetText());
                        inputBox.ShowDialog();

                        if (inputBox.OKPressed == true)
                        {
                            at.SetTitle(inputBox.title);
                            at.SetText(inputBox.description);
                        }
                        else
                        {
                            annotationList.Remove(at);
                            break;
                        }

                        inputBox.Close();
                        pb.Parent.Focus();
                        inputBox = null;
                    }

                }
            }

        if (!objChanged)
        {
            if (x2 - x1 > MIN_WIDTH &&
                y2 - y1 > MIN_HEIGHT)
            {
                AnnotationInput inputBox = new AnnotationInput();
                inputBox.ShowDialog();

                if (inputBox.OKPressed == true)
                    annotationList.AddLast(new RectangleType(x1 + hScrollBar.Value,
                                                             y1 + vScrollBar.Value,
                                                             x2 - x1,
                                                             y2 - y1,
                                                             inputBox.title,
                                                             inputBox.description)
                                           );

                inputBox.Dispose();
            }
            else if (x2 - x1 == 0 &&
                     y2 - y1 == 0)
            {
                // This is a simple click....
                // Ignore this.
            }
            else
            {
                MessageBox.Show("Invalid dimmenstions.",
                                "Smart Annotation",
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Exclamation);
            }
        }

        // Reset classState
        classState = stateType.NONE;

        ((PictureBox)sender).Refresh();
    }

        public void Paint_Handler(object sender, PaintEventArgs e)
        {
            Graphics gr = e.Graphics;
            int x1, x2;
            int y1, y2;

            gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

            // Draw image at right scroll
            gr.DrawImage(pb.Image,
                         new Rectangle(-hScrollBar.Value,
                                       -vScrollBar.Value,
                                       pb.Image.Width,
                                       pb.Image.Height));

            // Draw all annotations
            if (mouseOnImage)
            {
                foreach (AnnotationType at in annotationList)
                {
                    at.UpdateScroll(hScrollBar.Value, vScrollBar.Value);

                    if (at.IsInBottomBorder(mouseX, mouseY) ||
                        at.IsInLeftBorder(mouseX, mouseY) ||
                        at.IsInMiddle(mouseX, mouseY) ||
                        at.IsInRightBorder(mouseX, mouseY) ||
                        at.IsInTopBorder(mouseX, mouseY))
                    {
                        at.Draw(gr, true);
                        int x, y, w, h;
                        x = 0;
                        y = pb.Height - 70;
                        w = pb.Width;
                        h = 70;
                        DrawAnnotationDescription(gr, at.GetTitle(), at.GetText(), x, y, w, h);
                    }
                    else
                    {
                        at.Draw(gr, false);
                    }
                }
            }

            if (isMouseDown && classState == stateType.DRAW)
            {
                Pen dashPen = new Pen(Color.FromArgb(250, Color.Gray), 1f);
                dashPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;

                if (mouseDownX < mouseX)
                {
                    x1 = mouseDownX;
                    x2 = mouseX;
                }
                else
                {
                    x2 = mouseDownX;
                    x1 = mouseX;
                }

                if (mouseDownY < mouseY)
                {
                    y1 = mouseDownY;
                    y2 = mouseY;
                }
                else
                {
                    y2 = mouseDownY;
                    y1 = mouseY;
                }

                gr.DrawRectangle(dashPen, x1, y1, x2 - x1, y2 - y1);
            }
        }

        private void vScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            Graphics g = pb.CreateGraphics();
            g.DrawImage(pb.Image,
                        new Rectangle(-hScrollBar.Value,
                                      -vScrollBar.Value,
                                      pb.Image.Width,
                                      pb.Image.Height));
        }

        private void hScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            Graphics g = pb.CreateGraphics();
            g.DrawImage(pb.Image,
                        new Rectangle(-hScrollBar.Value,
                                      -vScrollBar.Value,
                                      pb.Image.Width,
                                      pb.Image.Height));
        }

        private void pictureBox_SizeChanged(object sender, EventArgs e)
        {
            // Set scrollbars to right value.
            this.vScrollBar.Minimum = 0;
            if (pb.Height >= pb.Image.Height)
                this.vScrollBar.Maximum = 0;
            else
                this.vScrollBar.Maximum = pb.Image.Height - pb.Height;
            if (this.vScrollBar.Value > this.vScrollBar.Maximum)
                this.vScrollBar.Value = this.vScrollBar.Maximum;

            this.hScrollBar.Minimum = 0;
            if (pb.Width >= pb.Image.Width)
                this.hScrollBar.Maximum = 0;
            else
                this.hScrollBar.Maximum = pb.Image.Width - pb.Width;
            if (this.hScrollBar.Value > this.hScrollBar.Maximum)
                this.hScrollBar.Value = this.hScrollBar.Maximum;

            // Draw image at right scroll
            Graphics gr = pb.CreateGraphics();
            gr.DrawImage(pb.Image,
                         new Rectangle(-hScrollBar.Value,
                                       -vScrollBar.Value,
                                       pb.Image.Width,
                                       pb.Image.Height));
        }

        #endregion

    }
}